篇首语:本文由编程笔记#小编为大家整理,主要介绍了Window与其管理者的秘密相关的知识,希望对你有一定的参考价值。
Window,字面意义即窗口,在android中,所有的视图都是通过Window来呈现的,Window是View的直接管理者。
Window是一个抽象类,具体实现是PhoneWindow。对Wndow的管理工作操作都是通过它的Manager–>WindowManager来做的,然而WindowManager也只是走个形式,真正的执行者是WindowManagerService,WindowManager通过提交工单的形式(IPC)交个WindowManagerService执行。
WindowManager管理Window的方式也很简单,常用的只有三种方法–>增删改(crud工程师),即addView(),updateViewLayout(),removeView()。这三个方法定义在ViewManager中,WIndowManager正是继承了ViewManager。
/**
* path: /frameworks/base/core/java/android/view/ViewManager.java
*/
public interface ViewManager
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
接下来看看WindowManager是如何将安排Window安排的明明白白的
本文分析的Android源码版本:android-10.0.0_r40
每个Window对应一个View和ViewRootImpl,Window和View通过ViewRootImpl建立联系,所以Window是以View的形式存在的
管理Window,需要借助WindowManager提供的方法,WindowManager是一个接口,真正的实现类是WindowManagerImpl,WindowManagerImpl中对这三个操作的实现如下:
/**
*path: /frameworks/base/core/java/android/view/WindowManagerImpl.java
*/
public final class WindowManagerImpl implements WindowManager
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params)
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params)
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
@Override
public void removeView(View view)
mGlobal.removeView(view, false);
从上述的源码中可以看到WindowManagerImpl并没有亲自处理这些操作,而是全部交给了它的秘书(WindowManagerGlobal)来做,WindowManagerImpl这种工作方式被称为桥接模式,WindowManagerGlobal通过单例模式向外提供实例,
/**
* path: /frameworks/base/core/java/android/view/WindowManagerGlobal.java
*/
public final class WindowManagerGlobal
@UnsupportedAppUsage
private static WindowManagerGlobal sDefaultWindowManager;
private WindowManagerGlobal()
//单例模式,对外返回唯一实例
@UnsupportedAppUsage
public static WindowManagerGlobal getInstance()
synchronized (WindowManagerGlobal.class)
if (sDefaultWindowManager == null)
sDefaultWindowManager = new WindowManagerGlobal();
return sDefaultWindowManager;
WindowManagerGlobal 的addView() 方法源码如下:
/**
* path: /frameworks/base/core/java/android/view/WindowManagerGlobal.java
*/
//存储所有Window对应的View
@UnsupportedAppUsage
private final ArrayList<View> mViews &#61; new ArrayList<View>();
//存储所有Window对应的ViewRootImpl
&#64;UnsupportedAppUsage
private final ArrayList<ViewRootImpl> mRoots &#61; new ArrayList<ViewRootImpl>();
//存储所有Window所对应的布局参数
&#64;UnsupportedAppUsage
private final ArrayList<WindowManager.LayoutParams> mParams &#61; new ArrayList<WindowManager.LayoutParams>();
//存储正在被删除的View对象(调用removeView()&#xff0c;但删除操作还未完成的Window)
private final ArraySet<View> mDyingViews &#61; new ArraySet<View>();
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow, int userId)
/* 1.检查参数的合法性&#xff0c;若是子Window还需调整布局参数 */
if (view &#61;&#61; null)
throw new IllegalArgumentException("view must not be null");
if (display &#61;&#61; null)
throw new IllegalArgumentException("display must not be null");
if (!(params instanceof WindowManager.LayoutParams))
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
final WindowManager.LayoutParams wparams &#61; (WindowManager.LayoutParams) params;
if (parentWindow !&#61; null)
parentWindow.adjustLayoutParamsForSubWindow(wparams);
else
// If there&#39;s no parent, then hardware acceleration for this view is
// set from the application&#39;s hardware acceleration setting.
final Context context &#61; view.getContext();
if (context !&#61; null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) !&#61; 0)
wparams.flags |&#61; WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
/* 第一步end */
/********************************************************/
/*2.创建ViewRootImpl&#xff0c;并将View加入到列表中*/
ViewRootImpl root;
View panelParentView &#61; null;
synchronized (mLock)
// Start watching for system property changes.
if (mSystemPropertyUpdater &#61;&#61; null)
mSystemPropertyUpdater &#61; new Runnable()
&#64;Override public void run()
synchronized (mLock)
for (int i &#61; mRoots.size() - 1; i >&#61; 0; --i)
mRoots.get(i).loadSystemProperties();
;
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
int index &#61; findViewLocked(view, false);
if (index >&#61; 0)
if (mDyingViews.contains(view))
// Don&#39;t wait for MSG_DIE to make it&#39;s way through root&#39;s queue.
mRoots.get(index).doDie();
else
throw new IllegalStateException("View " &#43; view
&#43; " has already been added to the window manager.");
// The previous removeView() had not completed executing. Now it has.
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >&#61; WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <&#61; WindowManager.LayoutParams.LAST_SUB_WINDOW)
final int count &#61; mViews.size();
for (int i &#61; 0; i < count; i&#43;&#43;)
if (mRoots.get(i).mWindow.asBinder() &#61;&#61; wparams.token)
panelParentView &#61; mViews.get(i);
root &#61; new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
/*第二步end*/
/********************************************************/
/*3.通过ViewRootImpl来更新界面并完成对Window的添加*/
// do this last because it fires off messages to start doing things
try
root.setView(view, wparams, panelParentView, userId);
catch (RuntimeException e)
// BadTokenException or InvalidDisplayException, clean up.
if (index >&#61; 0)
removeViewLocked(index, true);
throw e;
/*第三步end*/
/********************************************************/
WindowManagerGlobal 的addView() 方法主要做了这几件事&#xff1a;
/**
* path: /frameworks/base/core/java/android/view/ViewRootImpl.java
*/
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView)
setView(view, attrs, panelParentView, UserHandle.myUserId());
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId)
synchronized (this)
if (mView &#61;&#61; null)
mView &#61; view;
mAttachInfo.mDisplayState &#61; mDisplay.getState();
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
mViewLayoutDirectionInitial &#61; mView.getRawLayoutDirection();
mFallbackEventHandler.setView(view);
mWindowAttributes.copyFrom(attrs);
if (mWindowAttributes.packageName &#61;&#61; null)
mWindowAttributes.packageName &#61; mBasePackageName;
mWindowAttributes.privateFlags |&#61;
WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
attrs &#61; mWindowAttributes;
setTag();
if (DEBUG_KEEP_SCREEN_ON && (mClientWindowLayoutFlags
& WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) !&#61; 0
&& (attrs.flags&WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) &#61;&#61; 0)
Slog.d(mTag, "setView: FLAG_KEEP_SCREEN_ON changed from true to false!");
// Keep track of the actual window flags supplied by the client.
mClientWindowLayoutFlags &#61; attrs.flags;
setAccessibilityFocus(null, null);
if (view instanceof RootViewSurfaceTaker)
mSurfaceHolderCallback &#61;
((RootViewSurfaceTaker)view).willYouTakeTheSurface();
if (mSurfaceHolderCallback !&#61; null)
mSurfaceHolder &#61; new TakenSurfaceHolder();
mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
mSurfaceHolder.addCallback(mSurfaceHolderCallback);
// Compute surface insets required to draw at specified Z value.
// TODO: Use real shadow insets for a constant max Z.
if (!attrs.hasManualSurfaceInsets)
attrs.setSurfaceInsets(view, false /*manual*/, true /*preservePrevious*/);
CompatibilityInfo compatibilityInfo &#61; mDisplay.getDisplayAdjustments().getCompatibilityInfo();
mTranslator &#61; compatibilityInfo.getTranslator();
// If the application owns the surface, don&#39;t enable hardware acceleration
if (mSurfaceHolder &#61;&#61; null)
// While this is supposed to enable only, it can effectively disable
// the acceleration too.
enableHardwareAcceleration(attrs);
final boolean useMTRenderer &#61; MT_RENDERER_AVAILABLE
&& mAttachInfo.mThreadedRenderer !&#61; null;
if (mUseMTRenderer !&#61; useMTRenderer)
// Shouldn&#39;t be resizing, as it&#39;s done only in window setup,
// but end just in case.
endDragResizing();
mUseMTRenderer &#61; useMTRenderer;
boolean restore &#61; false;
if (mTranslator !&#61; null)
mSurface.setCompatibilityTranslator(mTranslator);
restore &#61; true;
attrs.backup();
mTranslator.translateWindowLayout(attrs);
if (DEBUG_LAYOUT) Log.d(mTag, "WindowLayout in setView:" &#43; attrs);
if (!compatibilityInfo.supportsScreen())
attrs.privateFlags |&#61; WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
mLastInCompatMode &#61; true;
mSoftInputMode &#61; attrs.softInputMode;
mWindowAttributesChanged &#61; true;
mAttachInfo.mRootView &#61; view;
mAttachInfo.mScalingRequired &#61; mTranslator !&#61; null;
mAttachInfo.mApplicationScale &#61; mTranslator &#61;&#61; null ? 1.0f : mTranslator.applicationScale;
if (panelParentView !&#61; null)
mAttachInfo.mPanelParentWindowToken
&#61; panelParentView.getApplicationWindowToken();
mAdded &#61; true;
int res; /* &#61; WindowManagerImpl.ADD_OKAY; */
/*完成异步刷新请求*/
/********************************************************/
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
/********************************************************/
InputChannel inputChannel &#61; null;
if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) &#61;&#61; 0)
inputChannel &#61; new InputChannel();
mForceDecorViewVisibility &#61; (mWindowAttributes.privateFlags & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) !&#61; 0;
/*通过WindowSession来添加Window*/
/********************************************************/
try
mOrigWindowType &#61; mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes &#61; true;
collectViewAttributes();
adjustLayoutParamsForCompatibility(mWindowAttributes);
res &#61; mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
setFrame(mTmpFrame);
catch (RemoteException e)
mAdded &#61; false;
mView &#61; null;
mAttachInfo.mRootView &#61; null;
inputChannel &#61; null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
finally
if (restore)
attrs.restore();
/********************************************************/
if (mTranslator !&#61; null)
mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout);
mAttachInfo.mAlwaysConsumeSystemBars &#61;
(res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS) !&#61; 0;
mPendingAlwaysConsumeSystemBars &#61; mAttachInfo.mAlwaysConsumeSystemBars;
mInsetsController.onStateChanged(mTempInsets);
mInsetsController.onControlsChanged(mTempControls);
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " &#43; mWindow);
if (res < WindowManagerGlobal.ADD_OKAY)
mAttachInfo.mRootView &#61; null;
mAdded &#61; false;
mFallbackEventHandler.setView(null)